/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.appspot.trafficando.infrastructure.provider;

import com.appspot.trafficando.domain.EventCategory;
import com.appspot.trafficando.domain.EventProvider;
import com.appspot.trafficando.domain.EventType;
import com.appspot.trafficando.domain.UrlConnection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.abdera.ext.geo.Position;
import org.apache.abdera.ext.rss.RssFeed;
import org.apache.abdera.ext.rss.RssItem;
import org.apache.abdera.model.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionService;
import static com.appspot.trafficando.util.AtomHelper.*;
/**
 *
 * @author Domenico Maria Giffone
 */
public abstract class RSSEventProvider implements EventProvider {

    private static final Logger LOG = LoggerFactory.getLogger(RSSEventProvider.class);
    private String location;
    private List<String> countries;
    private EventType eventType;
    private String name;
    private ConversionService converter;
    private String contributor;
    private int timeout = 10;
    private UrlConnection connection;

    public void setUrlConnection(UrlConnection connection) {
        this.connection = connection;
    }

    /**
     * The timeout in seconds for remote connection
     * Maximum value is 10 seconds greater values are ignored
     * @param timeout in seconds
     */
    public void setTimeout(int timeout) {
        if (timeout > 0 || timeout < 11) {
            this.timeout = timeout;
        }
    }

    //@Autowired Disable autowired to reduce app startup
    public void setConverter(ConversionService converter) {
        this.converter = converter;
    }

    public void setSupportedCountries(List<String> countries) {
        this.countries = countries;
    }

    @Override
    public List<String> getSupportedCountries() {
        return Collections.unmodifiableList(countries);
    }

    public void setEventType(EventType eventType) {
        this.eventType = eventType;
    }

    @Override
    public EventType getEventType() {
        return eventType;
    }

    @Override
    public List<Entry> getEntries() {
        try {
            RssFeed f = (RssFeed) getParser()
                    .parse(connection.getRemoteStream(location, this.timeout * 1000))
                    .getRoot();
            return toAtomEntries(f);
        } catch (IOException ex) {
            LOG.warn(ex.getMessage());
            throw new RuntimeException("Unable to retrieve remote feed");
        }
    }

    public RSSEventProvider() {
    }

    public RSSEventProvider(String name, String contributor) {
        this.name = name;
        this.contributor = contributor;
    }

    public void setRemoteAddress(String location) {
        this.location = location;
    }

    @Override
    public String getRemoteAddress() {
        return this.location;
    }

    @Override
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getContributor() {
        return contributor;
    }

    public void setContributor(String contributor) {
        this.contributor = contributor;
    }

    private List<Entry> toAtomEntries(RssFeed f) {
        final int itemsSize = f.getEntries().size();
        LOG.debug("trying to convert to atom {} RSS items", itemsSize);
        List<Entry> result = new ArrayList<Entry>();
        for (Entry rssEntry : f.getEntries()) {
            RssItem item = (RssItem) rssEntry;
            Entry atomEntry = convert(item);
            if (null != atomEntry) {
                result.add(atomEntry);
            }
        }
        final int entriesSize = result.size();
        LOG.debug("About to return {} entries", entriesSize);
        if (itemsSize > entriesSize) {
            LOG.warn("{} rss items missed during conversion", itemsSize - entriesSize);
        }
        return Collections.unmodifiableList(result);
    }

    protected Entry convert(RssItem item) {
        Entry result = getEmptyEntry();
        addType(result, eventType);
        result.addContributor(this.contributor);
        copyEntry(item, result);
        Position[] positions = org.apache.abdera.ext.geo.GeoHelper.getPositions(item);
        if (null == positions || positions.length < 1 || null == positions[0]) {
            LOG.debug("Can't find position! Ignoring entry with title {}", result.getTitle());
            return null;
        }
        addCategory(result);
        return result;

    }

    protected void addCategory(Entry entry) {
        sanitizeEntry(entry);
        List<EventCategory> categories = getCategories(entry);
        for (EventCategory category : categories) {
            entry.addCategory(category.getScheme(), category.getTerm(), category.getLabel());
        }
    }

    protected abstract List<EventCategory> getCategories(Entry entry);

    protected abstract void sanitizeEntry(Entry entry);
}
